package com.demo.common.client;

import java.lang.reflect.Type;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;

import com.alibaba.fastjson2.JSON;
import com.demo.common.util.TracerUtil;
import com.demo.common.util.httpclient.Executor;
import com.demo.common.util.httpclient.HttpExecutor;

/**
 * AbstractHttpClient
 * 
 * @author demo
 * @data 2020-12-01 11:15:38
 */
public abstract class AbstractHttpClient {
    private ThreadLocal<Pair<String, String>> action = new ThreadLocal<>();
    private ThreadLocal<Pair<Integer, Integer>> timeout = new ThreadLocal<>();
    private ThreadLocal<Map<String, String>> headers = new ThreadLocal<>();

    public abstract String getRequestServerCode(String function, Object[] args);

    public abstract String getRequestServerTitle(String function, Object[] args);

    protected abstract HttpExecutor getHttpExecutor();

    private synchronized HttpExecutor getHttpExecutorInternal() {
        return getHttpExecutor();
    }

    protected void setAction(String code, String title) {
        this.action.set(Pair.of(code, title));
    }

    public Pair<String, String> getAction() {
        return this.action.get();
    }

    protected void removeAction() {
        this.action.remove();
    }

    protected void setTimeout(Integer connectTimeout, Integer socketTimeout) {
        this.timeout.set(Pair.of(connectTimeout, socketTimeout));
    }

    protected void removeTimeout() {
        this.timeout.remove();
    }

    protected void setHeaders(Map<String, String> headers) {
        this.headers.set(headers);
    }

    protected void removeHeaders() {
        this.headers.remove();
    }

    protected <T> T get(String url, Map<String, Object> params, Class<T> clazz) throws Exception {
        String responseStr = get(url, params);
        return JSON.parseObject(responseStr, clazz);
    }

    protected <T> T get(String url, Map<String, Object> params, Type type) throws Exception {
        String responseStr = get(url, params);
        return JSON.parseObject(responseStr, type);
    }

    protected <T> T postForm(String url, Map<String, Object> params, Class<T> clazz) throws Exception {
        String responseStr = postForm(url, params);
        return JSON.parseObject(responseStr, clazz);
    }

    protected <T> T postForm(String url, Map<String, Object> params, Type type) throws Exception {
        String responseStr = postForm(url, params);
        return JSON.parseObject(responseStr, type);
    }

    protected <T> T postJson(String url, Object body, Class<T> clazz) throws Exception {
        String responseStr = postJson(url, body);
        return JSON.parseObject(responseStr, clazz);
    }

    protected <T> T postJson(String url, Object body, Type type) throws Exception {
        String responseStr = postJson(url, body);
        return JSON.parseObject(responseStr, type);
    }

    private String get(String url, Map<String, Object> params) throws Exception {
        HttpExecutor httpExecutor = getHttpExecutorInternal();
        Executor executor = httpExecutor.get(url).params(params);
        return send(executor);
    }

    private String postForm(String url, Map<String, Object> params) throws Exception {
        HttpExecutor httpExecutor = getHttpExecutorInternal();
        Executor executor = httpExecutor.postForm(url).params(params);
        return send(executor);
    }

    private String postJson(String url, Object body) throws Exception {
        HttpExecutor httpExecutor = getHttpExecutorInternal();
        Executor executor = httpExecutor.postJson(url).raw(body == null ? StringUtils.EMPTY : JSON.toJSONString(body));
        return send(executor);
    }

    private String send(Executor executor) throws Exception {
        Pair<Integer, Integer> pair = this.timeout.get();
        Integer connectTimeout = null;
        Integer socketTimeout = null;
        if (pair != null) {
            connectTimeout = pair.getLeft();
            socketTimeout = pair.getRight();
        }
        if (connectTimeout == null) {
            connectTimeout = 0;
        }
        if (socketTimeout == null) {
            socketTimeout = 0;
        }
        Map<String, String> headers = this.headers.get();
        executor.headers(headers).headers(TracerUtil.getTraceHeaders()).connectTimeout(connectTimeout).socketTimeout(socketTimeout).logContent();
        try {
            return executor.execute().getContentString();
        } finally {
            this.action.remove();
            this.timeout.remove();
        }
    }

}
